/**
 * 代理模式：为其它对象提供一种代理以控制这个对象的访问。在某些情况下，一个对象不适合或者不能直接引用另一个对象，而代理对象可以在客户端和目标对象之间起到中介作用。
 * 代理模式适合应用场景:
 * 1. 延迟初始化（虚拟代理）。如果你有一个偶尔使用的重量级服务对象，一直保持该对象运行会消耗系统资源时，可使用代理模式。
 * 2. 访问控制（保护代理）。如果你只希望特定客户端使用服务对象，这里的对象可以是操作系统中非常重要的部分，而客户端则是各种已启动的程序（包括恶意程序），此时可使用代理模式。
 * 3. 本地执行远程服务（远程代理）。适用于服务对象位于远程服务器上的情形。
 * 4. 记录日志请求（日志记录代理）。适用于当你需要保存对于服务对象的请求历史记录时。代理可以在向服务传递请求前进行记录。
 * 5. 智能引用。可在没有客户端使用某个重量级对象时立即销毁该对象。代理会将所有获取了指向服务对象或其结果的客户端记录在案。代理会时不时地遍历各个客户端，检查它们是否仍在运行。如果相应的客户端列表为空，代理就会销毁该服务对象，释放底层系统资源。
 * 优点：
 * 1. 职责清晰。真实的角色只负责实现实际的业务逻辑，不用关心其它非本职责的事务，通过后期的代理完成具体的任务。这样代码会简洁清晰。
 * 2. 如果客户端对服务对象的生命周期没有特殊要求，可以对生命周期进行管理。
 * 3. 即使服务对象还未准备好或不存在，代理也可以正常工作。
 * 4. 开闭原则。可以在不对服务或客户端做出修改的情况下创建新代理。
 */
#pragma once

#include <iostream>
#include <memory>

using namespace std;

namespace proxy
{
// Subject 接口声明了 RealSubject 和 Proxy 的通用操作。只要客户端使用此接口与 RealSubject 一起工作，就可以向它传递代理而不是真正的 Subject。
class Subject {
public:
    virtual void Request() const = 0;
};

// RealSubject 包含一些核心业务逻辑。通常，RealSubject 能够做一些有用的工作，这些工作也可能非常缓慢或敏感，例如，纠正输入数据。代理无需对 RealSubject 的代码进行任何修改而解决这些问题。
class RealSubject : public Subject {
public:
    void Request() const override {
        cout << "RealSubject: Handling request." << endl;
    }
};

// 代理具有与 RealSubject 相同的接口。
class Proxy : public Subject {
// 代理维护对 RealSubject 类的对象的引用。它可以是延迟加载的，也可以由客户端传递给代理。
public:
    Proxy(shared_ptr<RealSubject> realSubject) : m_realSubject(realSubject) {}

    // 代理模式最常见的应用是延迟加载、缓存、控制访问、日志记录等。代理可以执行这些操作之一，然后根据结果将执行传递给链接的 RealSubject 对象中的相同方法.
    void Request() const override {
        if (CheckAccess()) {
            m_realSubject->Request();
            LogAccess();
        }
    }

private:
    bool CheckAccess() const {
        // 一些真正的检查应该在这里进行。
        cout << "Proxy: Checking access prior to firing a real request." << endl;
        return true;
    }
    void LogAccess() const {
        cout << "Proxy: Logging the time of request." << endl;
    }

private:
    shared_ptr<RealSubject> m_realSubject;
};

void ClientCode()
{
  cout << "Client: Executing the client code with a real subject:" << endl;
  shared_ptr<RealSubject> real_subject(new RealSubject);
  real_subject->Request();
  cout << "Client: Executing the same client code with a proxy:" << endl;
  shared_ptr<Proxy> proxy(new Proxy(real_subject));
  proxy->Request();
}
}   //namespace proxy